package teleware.soe.core.context;

import teleware.soe.core.annotation.Autowired;
import teleware.soe.core.annotation.ComponentScan;
import teleware.soe.core.annotation.Controller;
import teleware.soe.core.dto.BeanDefinition;
import teleware.soe.core.dto.MappingPool;
import teleware.soe.core.enums.MappingEnum;
import teleware.soe.core.util.ScanUtils;

import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;

/**
 * @author :hongsw
 * @date :2020-12-10 17:44
 */
public class ApplicationContext {
    /**
     * 单例池
     */
    private final ConcurrentHashMap<String, Object> singletonPool = new ConcurrentHashMap<String, Object>();
    /**
     * 扫描Bean池
     */
    private final ConcurrentHashMap<String, BeanDefinition> beanDefinitionPool = new ConcurrentHashMap<String, BeanDefinition>();
    /**
     * 映射池
     */
    private final MappingPool mappingPool = new MappingPool();

    /**
     * 初始化
     *
     * @param configClass 配置类
     * @author hongsw
     * @date 2020/12/9 17:25
     **/
    public ApplicationContext(Class<?> configClass) {
        // 1.判断是否有配置扫描注解
        if (!configClass.isAnnotationPresent(ComponentScan.class)) {
            throw new RuntimeException("没有配置扫描注解");
        }
        // 2.扫描类
        ComponentScan annotation = configClass.getAnnotation(ComponentScan.class);
        // 需要扫描的包路径
        String scanPath = annotation.value();
        // 扫描路径下的所有类
        listMethodMapping(scanPath);
    }


    private void listMethodMapping(String scanPath) {
        ArrayList<Class<? extends Annotation>> classes = new ArrayList<Class<? extends Annotation>>();
        classes.add(Controller.class);
        try {
            // 扫描出需要生成Bean的全限定类名
            List<String> controllerList = ScanUtils.scan(scanPath, classes);
            for (String fullyQualifiedClassName : controllerList) {
                // 处理扫描结果
                processScanResults(fullyQualifiedClassName);
            }
        } catch (Exception e) {
            throw new RuntimeException(e.getMessage());
        }
    }

    /**
     * 处理扫描结果
     *
     * @param fullyQualifiedClassName 全限定类名
     * @author hongsw
     * @date 2020/12/11 15:23
     **/
    private void processScanResults(String fullyQualifiedClassName) throws ClassNotFoundException {
        //根据全限定类名反射类
        Class<?> clazz = Class.forName(fullyQualifiedClassName);
        // 配置Bean定义
        BeanDefinition beanDefinition = new BeanDefinition();
        beanDefinition.setBeanClass(clazz);
        beanDefinitionPool.put(fullyQualifiedClassName, beanDefinition);
        //创建Bean
        Object bean = createBean(fullyQualifiedClassName);
        singletonPool.put(fullyQualifiedClassName, bean);
        for (Method method : clazz.getMethods()) {
            // 策略模式-处理映射
            for (MappingEnum value : MappingEnum.values()) {
                if (method.isAnnotationPresent(value.getAnnotationClass())) {
                    MappingStrategyContext mappingStrategyContext = new MappingStrategyContext();
                    mappingStrategyContext.process(value, method, mappingPool);
                    break;
                }
            }
        }
    }

    /**
     * 创建Bean
     *
     * @param beanName 全限定类名
     * @return java.lang.Object
     * @author hongsw
     * @date 2020/12/11 15:18
     **/
    public Object createBean(String beanName) {
        BeanDefinition beanDefinition = beanDefinitionPool.get(beanName);
        Class<?> beanClass = beanDefinition.getBeanClass();
        Object bean = null;
        try {
            // 实例化
            bean = beanClass.getDeclaredConstructor().newInstance();
            // 填充属性
            Field[] fields = beanClass.getDeclaredFields();
            for (Field field : fields) {
                if (field.isAnnotationPresent(Autowired.class)) {
                    Object fieldBean = getBean(field.getType().getName());
                    field.setAccessible(true);
                    field.set(bean, fieldBean);
                }
            }
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        }
        return bean;
    }

    /**
     * 获取Bean
     *
     * @param beanName 获取bean
     * @return java.lang.Object
     * @author hongsw
     * @date 2020/12/11 15:20
     **/
    public Object getBean(String beanName) {
        Object bean = singletonPool.get(beanName);
        if (null == bean) {
            bean = createBean(beanName);
            singletonPool.put(beanName, bean);
        }
        return bean;
    }

    public MappingPool getMappingPool() {
        return mappingPool;
    }
}
